#include "../include/paginacion.h"
#include "../include/string.h"
#include "../include/kasm.h"

/*
 * DEF. VARIABLES GLOBALES
 */


 /* Directorio y entradas del directorio de paginas */
unsigned long * page_directory = (unsigned long *) DIR_ADDRESS;

unsigned long * page_krnlTable = (unsigned long *) (DIR_ADDRESS + PAGE_SIZE);

unsigned long * page_filesysTable = (unsigned long *) (DIR_ADDRESS + 2 * PAGE_SIZE);

unsigned long * page_userTable = (unsigned long *) (DIR_ADDRESS + 3 * PAGE_SIZE);

/* pageOwner[i] = j, proceso de usuario con pid j, tiene asignado la pagina i-esima
* en la tabla de paginas del usuario. pageOwner[i] = -1, pagina no asignada aun.*/
int pageOwner[MAX_PAGES];

/*
 * FUNCIONES:
 */

/* Inicia en -1 todas las posiciones de pageOwner.*/
void
initPageStatus()
{
	memset((void *)pageOwner, -1, MAX_PAGES * sizeof(int));
}

void
initPaging()
{
	int i;
	unsigned long address;

	for (i = 0 ; i < MAX_PAGES ; i++)
	{
		page_krnlTable[i] = address | 3;
		address += PAGE_SIZE; // Mantiene cada página alineada a 4kb.
	}

	for (i = 0 ; i < MAX_PAGES ; i++)
	{
		page_filesysTable[i] = address | 3;
		address += PAGE_SIZE; // Mantiene cada página alineada a 4kb.
	}

	for (i = 0 ; i < MAX_PAGES ; i++)
	{
		// Bit 0 = Present/Not-Present. Inicialmente P = 0.
		page_userTable[i] = address & 0xFFFFFFFE;
		address += PAGE_SIZE;
	}

	page_directory[0] = (unsigned long)page_krnlTable;
	page_directory[0] |= 0x03; // (011 en binario) setea U/S = 0, R/W = 1, P = 1.
	page_directory[1] = (unsigned long)page_filesysTable;
	page_directory[1] |= 0x03;
	page_directory[2] = (unsigned long)page_userTable;
	page_directory[2] |= 0x07; // U/S = 1, R/W = 1, P = 1.

	for (i = 3 ; i < MAX_PAGES ; i++)
	{
		// Deshabilita el resto de las entradas, U/S = 0, R/W = 1, P = 0.
		page_directory[i] = 0 | 2;
	}

	/* Escribe al registro cr3 la dirección base de la tabla de directorios,
	 * y setea en 1 el bit 31 del cr0 = Habilitar la paginación.*/

	_write_cr3(page_directory);
	_write_cr0((unsigned long *) (_read_cr0() | 0x80000000));
}

int
enablePage(unsigned long * table, int pagenum)
{
	int success;
	unsigned long alreadyEnabled;

	if (pagenum < 0 || pagenum >= MAX_PAGES)
		return 0;

	alreadyEnabled = page_userTable[pagenum] & 0x01;

	if (alreadyEnabled)
	{
		success = 0;
	}
	else
	{
		page_userTable[pagenum] |= 0x07; // U/S = U, R/W = ambos, P = present.
		success = 1;
	}

	return success;
}

int
disablePage(unsigned long * table, int pagenum)
{
	unsigned long disable;

	if (pagenum < 0 || pagenum >= MAX_PAGES)
		return 0;

	disable = 0xFFFFFFFE;
	page_userTable[pagenum] &= disable; // U/S = ?, R/W = ?, P = not present.


	return 1;
}

int
GetPageNumByPID(int pid)
{
	int i, found = 0;
	int page = -1;

	for (i = 0 ; i < MAX_PAGES && !found ; i++)
	{
		if (pageOwner[i] == pid)
		{
			found = 1;
			page = i;
		}
	}

	return page;
}
